package com.youlu.campus.web.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.AccountException;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.common.exception.WechatException;
import com.youlu.campus.component.SmsUtil;
import com.youlu.campus.entity.ActivityInfo;
import com.youlu.campus.entity.AppUserPO;
import com.youlu.campus.entity.PlatformConfig;
import com.youlu.campus.entity.QQH5UserPO;
import com.youlu.campus.entity.QQMiniUserPO;
import com.youlu.campus.entity.SmsTemplate;
import com.youlu.campus.entity.UniversityInfo;
import com.youlu.campus.entity.UserInfoExtend;
import com.youlu.campus.entity.UserOtherInfo;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.entity.VO.req.CheckMobileVO;
import com.youlu.campus.entity.VO.req.WechatH5CodeReqNewVo;
import com.youlu.campus.entity.VO.req.WechatH5CodeReqVO;
import com.youlu.campus.entity.VO.res.UserResponse;
import com.youlu.campus.entity.WechatH5UserPO;
import com.youlu.campus.entity.WechatMiniUserPO;
import com.youlu.campus.entity.domain.QqMovementStep;
import com.youlu.campus.entity.domain.QqMovementStepDomain;
import com.youlu.campus.entity.domain.WechatAccessToken;
import com.youlu.campus.entity.dto.sms.SmsBaseInfoDTO;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.auth.WechatH5UserService;
import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.service.cache.CacheActivityService;
import com.youlu.campus.service.cache.CacheUserService;
import com.youlu.campus.service.h5.H5UserPOService;
import com.youlu.campus.service.platform.PlatformConfigService;
import com.youlu.campus.service.qq.QQMiniUserService;
import com.youlu.campus.service.ranking.impl.RankingServiceImpl;
import com.youlu.campus.service.sms.SmsServerService;
import com.youlu.campus.service.sms.SmsTemplateService;
import com.youlu.campus.service.university.UniversityService;
import com.youlu.campus.service.utils.WebTokenUtils;
import com.youlu.campus.service.visitor.UserInfoExtendService;
import com.youlu.campus.service.visitor.UserOtherInfoService;
import com.youlu.campus.service.visitor.VisitorUserService;
import com.youlu.campus.service.wechat.SystemNickNameAndHeaderImageService;
import com.youlu.campus.service.wechat.WechatMiniService;
import com.youlu.campus.service.wechat.WechatService;
import com.youlu.campus.web.common.base.RequestContext;
import com.youlu.campus.web.common.utils.CommonUtils;
import com.youlu.campus.web.community.entity.UserFollow;
import com.youlu.campus.web.university.dto.UniversityResponse;
import com.youlu.campus.web.university.entity.UniversityPO;
import com.youlu.campus.web.user.dto.request.DecryptRequest;
import com.youlu.campus.web.user.dto.request.UserHomeRequest;
import com.youlu.campus.web.user.dto.request.UserInfoRequest;
import com.youlu.campus.web.user.dto.response.DecryptResult;
import com.youlu.campus.web.user.dto.response.UserHomeResponse;
import com.youlu.campus.web.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.Security;
import java.util.Date;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * C端用户接口实现类
 *
 * @author: zhuhuaiqi
 * @date: 2020/10/31 14:01
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseService implements UserService {
    @Autowired
    private VisitorUserService visitorUserService;
    public static final String TOKEN_KEY = "youlu:dt:";
    public static final String TOKEN_KEY_WECHAT_H5 = "youlu:dt:wechat:h5:%s";
    public static final String ACCESS_TOKEN_PRE_FIX = "com.luyou.access-token.%s";
    public static final String ACCESS_TOKEN_WECHAT_H5_PRE_FIX = "com.luyou.wechat.h5.access-token.%s.%s";
    public static final String CODE_WECHAT_H5_PRE_FIX = "com.luyou.wechat.h5.code.%s.%s";
    public static final String CLIENT_WECHAT_H5_IP_ERROR_PRE_FIX = "com.luyou.wechat.h5.appId.%s.%s";
    // 静默授权
    private static final String SCOP_BASE = "snsapi_base";
    // 非静默授权
    private static final String SCOP_USER_INFO = "snsapi_userinfo";

    @Value("${sys.qq.secret}")
    private String secret;
    // 微信公众号AppId
    @Value("${sys.pay.wx.wxAppId}")
    private String wxAppId;
    @Value("${sys.pay.wx.wxAppSecret}")
    private String wxAppSecret;
    @Value("${sys.pay.wx.bizDomain}")
    private String bizDomain;
    @Value("${sys.pay.wx.bizFrontDomain}")
    private String bizFrontDomain;

    @Value("${sys.pay.wx.bizCompetitionFrontDomain}")
    private String bizCompetitionFrontDomain;

    @Value("${sys.pay.wx.bizFrontPayDomain}")
    private String bizFrontPayDomain;

    @Autowired
    private SmsUtil smsUtil;
    @Autowired
    private SmsTemplateService smsTemplateService;

    @Autowired
    @Lazy
    private ActivityService activityService;

    @Autowired
    private CacheActivityService cacheActivityService;

    @Lazy
    @Autowired
    private CacheUserService cacheUserService;
    @Lazy
    @Autowired
    private WechatH5UserService wechatH5UserService;

    @Autowired
    private PlatformConfigService platformConfigService;
    @Autowired
    private UserOtherInfoService userOtherInfoService;
    @Lazy
    @Autowired
    private QQUserService qQUserService;
    @Lazy
    @Autowired
    private H5UserPOService h5UserPOService;
    @Lazy
    @Autowired
    private WechatMiniService wechatMiniService;
    @Autowired
    private RankingServiceImpl rankingServiceImpl;
    @Autowired
    private SystemNickNameAndHeaderImageService systemNickNameAndHeaderImageService;
    @Autowired
    private QQMiniUserService qqMiniUserService;

    @Autowired
    private SmsServerService smsServerService;

    @Autowired
    private UserInfoExtendService userInfoExtendService;

    @Autowired
    private UniversityService universityService;

    @Resource(name = "masterMongoTemplate")
    private MongoTemplate masterMongoTemplate;
    @Autowired
    private Environment environment;

    // QQ  小程序登陆地址
    private String QQ_MINI_LOGIN_URL = "https://api.q.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";

    static {
        //BouncyCastle是一个开源的加解密解决方案，主页在http://www.bouncycastle.org/
        Security.addProvider(new BouncyCastleProvider());
    }


    @Override
    public UserPO insert(UserPO userPO) {
        userPO = mongoTemplate.insert(userPO);
        return userPO;
    }

    @Override
    public UserPO decrypt(DecryptRequest decryptRequest) {
        log.info(":>>> 开始解密用户数据:{}", JSON.toJSONString(decryptRequest));
        decryptRequest.setAppId(RequestContext.getAppId());
        UserPO user = RequestContext.getCurUser();
        if (user == null) {
            log.info(":>>> 用户未登陆");
            throw new BusinessException("用户未登录~");
        }
        if (Objects.isNull(decryptRequest)) {
            log.error(":>>> 解密用户数据参数异常");
            return user;
        }
        String requestOpenId = user.getOpenId();
        log.info("解密微信用户信息开始,openId:{}", requestOpenId);
        if (org.springframework.util.StringUtils.isEmpty(requestOpenId)) {
            throw new BusinessException("用户信息错误~");
        }
        String sessionKey = qqMiniUserService.getSessionKey(decryptRequest.getAppId(), requestOpenId);
        if (StringUtils.isBlank(sessionKey)) {
            log.info("session key not found , {}", requestOpenId);
            throw new BusinessException("获取sessionKey失败，请重新登录");
        }
        log.info("sessionKey：{}", sessionKey);
        //解密之后的用户信息
        String decrypt = null;
        try {
            decrypt = CommonUtils.decrypt(decryptRequest.getEncryptedData(), sessionKey, decryptRequest.getIv(), "UTF" +
                    "-8");
        } catch (Exception e) {
            AccountException ex = new AccountException("解析用户信息异常");
            log.error("异常 {}", e);
            throw ex;
        }
        log.info("解析用户信息，结果为:{}", decrypt);
        if (StringUtils.isEmpty(decrypt)) {
            throw new AccountException("解析用户信息为空");
        }
        if (Objects.nonNull(decryptRequest.getType())) {
            switch (decryptRequest.getType()) {
                case 0:
                    DecryptResult decryptData = JSON.parseObject(decrypt, DecryptResult.class);
                    log.info(":>>> 解密的用户信息为:{}", JSON.toJSONString(decryptData));
                    QQMiniUserPO qqMiniUserPO = new QQMiniUserPO();
                    qqMiniUserPO.setAccountInfoId(user.getId());
                    qqMiniUserPO.setBinded(Boolean.TRUE);
                    qqMiniUserPO.setNickName(decryptData.getNickName());
                    qqMiniUserPO.setAvatarUrl(decryptData.getAvatarUrl());
                    qqMiniUserPO.setProvince(decryptData.getProvince());
                    qqMiniUserPO.setCity(decryptData.getCity());
                    qqMiniUserPO.setBinded(true);
                    qqMiniUserService.updateUserInfoByAccountId(qqMiniUserPO);
                    user = visitorUserService.getUser(user.getId());
                    log.info(":>>>  更新用户基础数据:{}", JSON.toJSONString(user));
                    return user;
                case 1:
                    QqMovementStepDomain movement = JSON.parseObject(decrypt, QqMovementStepDomain.class);
                    log.info(":>>> 解析的运动数据为:{}", JSON.toJSONString(movement));
                    if (Objects.nonNull(movement)) {
                        for (QqMovementStep qqMovementStep : movement.getStepInfoList()) {
                            if (Objects.nonNull(qqMovementStep.getTimestamp()) && Objects.nonNull(qqMovementStep.getStep())) {
                                if (qqMovementStep.getStep().compareTo(1) == -1) {
                                    qqMovementStep.setStep(this.getIntegerRandom(1000, 60));
                                }
                                visitorUserService.updateMovementStep(user.getId(),
                                        new Date(qqMovementStep.getTimestamp() * 1000), qqMovementStep.getStep());
                            }
                        }
                    }
                    return user;
                default:
                    break;
            }
        }
        return user;
    }

    @Override
    public UserPO queryUserByOpenId(String openId) {
        if (StringUtils.isBlank(openId)) {
            log.info(":>>> openId 为空");
            return null;
        }
        return cacheUserService.queryUserByOpenId(openId);
    }

    @Override
    public UserPO queryUserById(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        return cacheUserService.findById(id);
    }

    @Override
    public UserInfoRequest getUserInfo() {
        UserPO user = RequestContext.getCurUser();
        log.info("getUserInfo userId {}", user.getId());
        UserInfoRequest infoDTO = new UserInfoRequest();
        BeanUtils.copyProperties(user, infoDTO);
        if (StringUtils.isBlank(infoDTO.getSignature())) {
            infoDTO.setSignature(" ");
        }
        return infoDTO;
    }

    @Override
    public UniversityResponse userInfoPerfect(UserInfoRequest userInfo) {
        log.info("userInfoPerfect userInfo {}", userInfo);
        UserPO user = RequestContext.getCurUser();
        this.updateUser(user, userInfo);
        UniversityResponse response = new UniversityResponse();
        //获取学校信息
        Query uquery = new Query();
        uquery.addCriteria(Criteria.where("value").is(userInfo.getUniversityValue()));
        UniversityPO universityPO = mongoTemplate.findOne(uquery, UniversityPO.class);
        if (universityPO != null) {
            response.setUniversityValue(userInfo.getUniversityValue());
            response.setUniversityName(universityPO.getName());
            response.setLogo(universityPO.getLogo());
        }
        return response;
    }

    @Override
    public UserHomeResponse userInfo(UserHomeRequest request) {

        UserPO user = RequestContext.getCurUser();
        if (StringUtils.isBlank(request.getUserId())) {
            //当前用户信息
            UserHomeResponse response = mapperFacade.map(user, UserHomeResponse.class);
            if (StringUtils.isBlank(user.getSignature())) {
                response.setSignature("");
            } else {
                response.setSignature(user.getSignature());
            }
            response.setBinded(user.getBinded());
            response.setCheckedMobile(user.getCheckedMobile());
            return response;
        } else {
            UserPO userPO = queryUserById(request.getUserId());
            if (userPO == null) {
                throw new BusinessException("用户信息有误！");
            }
            UserHomeResponse response = mapperFacade.map(userPO, UserHomeResponse.class);
            response.setName("");
            response.setMobile("");
            Query query = new Query();
            query.addCriteria(Criteria.where("userId").is(request.getUserId()));
            query.addCriteria(Criteria.where("followerId").is(user.getId()));
            boolean followed = mongoTemplate.exists(query, UserFollow.class);
            response.setFollowed(followed);
            response.setBinded(user.getBinded());
            if (StringUtils.isBlank(userPO.getSignature())) {
                response.setSignature("");
            } else {
                response.setSignature(userPO.getSignature());
            }
            response.setBinded(user.getCheckedMobile());
            response.setCheckedMobile(user.getCheckedMobile());
            return response;
        }
    }

    @Override
    public String getWechatH5CodeByAppId(WechatH5CodeReqVO req) {
        log.info(":>>> 开始获取微信H5 Code链接, scope - > {}", req.getScope());
        ActivityInfo activityInfo = cacheActivityService.findById(req.getActivityId());
        //公众号登录无活动ID情况
        if (Objects.nonNull(req.getType()) && req.getType() != 1000) {
            if (Objects.isNull(activityInfo) || StringUtils.isBlank(activityInfo.getThemeColor())) {
                log.error(":>>> 活动:{} 不存在,或颜色配置:{}", req.getActivityId(), activityInfo.getThemeColor());
                throw new BusinessException("活动不存在,或颜色配置错误");
            }
        } else {
            activityInfo = new ActivityInfo();
        }
        //目录
        String profile = environment.getProperty("spring.profiles.active");
        String cate = "";
        if ("beta".equals(profile)) {
            cate = "/h4-beta/";
        } else {
            cate = platformConfigService.getCatalogue();
        }
        String redirectPage = "#/pages/user-login";
        if ("contest-plus".equals(activityInfo.getType())) {
            cate = platformConfigService.getContestPlusCatalogue();
        }
        if ("vote".equals(activityInfo.getType())) {
            cate = platformConfigService.getVoteCatalogue();
        }
        if (StringUtils.isNotBlank(req.getRedirectPage())) {
            redirectPage = req.getRedirectPage();
        }
        String scope = req.getScope();
        Integer rewardType = req.getRewardType();
        String redirect_uri = "";
        String extension = req.getExtension();
        try {
            if (StringUtils.isEmpty(scope) || scope.equals(SCOP_USER_INFO)) {
                scope = "snsapi_userinfo";
                if (StringUtils.isNotBlank(extension)) {
                    redirect_uri =
                            URLEncoder.encode(platformConfigService.getBizFrontDomain() + cate + redirectPage + "?activityId=" + req.getActivityId() + "&appId=" + req.getAppId() + "&type=" + req.getType() + "&theme=" + activityInfo.getThemeColor() + "&scope=" + scope + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8");
                } else {
                    redirect_uri =
                            URLEncoder.encode(platformConfigService.getBizFrontDomain() + cate + redirectPage + "?activityId=" + req.getActivityId() + "&appId=" + req.getAppId() + "&type=" + req.getType() + "&theme=" + activityInfo.getThemeColor() + "&scope=" + scope + "&rewardType=" + rewardType, "UTF-8");
                }

            } else {
                // 竞赛类H5,并且是收款活动,使用专用目录
                if ("contest-plus".equals(activityInfo.getType())) {
                    cate = platformConfigService.getContestPlusPayCatalogue();
                }
                // 替换收款专用域名

                if ("beta".equals(profile)) {
                    cate = "/h4-beta/";
                } else {
                    cate = platformConfigService.getPayCatalogue();
                }
                if (StringUtils.isNotBlank(extension)) {
                    if ("v2".equals(req.getVersion())) {
                        redirect_uri =
                                URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward/index?activityId=" + req.getActivityId() +
                                        "&appId=" + req.getAppId() + "&scope=" + scope + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8");
                    } else {
                        redirect_uri =
                                URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward?activityId=" + req.getActivityId() +
                                        "&appId=" + req.getAppId() + "&scope=" + scope + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8");
                    }

                } else {
                    if ("v2".equals(req.getVersion())) {
                        redirect_uri =
                                URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward/index?activityId=" + req.getActivityId() +
                                        "&appId=" + req.getAppId() + "&scope=" + scope + "&rewardType=" + rewardType, "UTF-8");
                    } else {
                        redirect_uri =
                                URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward?activityId=" + req.getActivityId() +
                                        "&appId=" + req.getAppId() + "&scope=" + scope + "&rewardType=" + rewardType, "UTF-8");
                    }
                }

            }
            log.info("appId - > {} scope - >{} redirect_uri - >{}", req.getAppId(), scope, redirect_uri);
            return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + req.getAppId() + "&redirect_uri="
                    + redirect_uri + "&response_type=code&scope="
                    + scope + "&state=STATE#wechat_redirect";
        } catch (Exception e) {
            log.error(":>>> 获取解码错误:{}", e);
            return "";
        }
    }

    @Override
    public String getWechatH5CodeUrlByAppId(WechatH5CodeReqNewVo req) {
        String cate;
        String domain;
        PlatformConfig config = platformConfigService.find();
        if (req.getType() != null && req.getType() == 1) {
            cate = config.getJinCatalogue();
            domain = config.getJinBizFrontDomain();
        } else {
            cate = config.getCardCatelogue();
            domain = config.getBizFrontPayDomain();
        }
        StringBuilder sb = new StringBuilder();
        sb.append(domain);
        sb.append(cate);
        sb.append(req.getPath());
        sb.append("?appId=" + req.getAppId());
        String scope = req.getScope();
        if (StringUtils.isEmpty(scope) || scope.equals(SCOP_USER_INFO)) {
            scope = SCOP_USER_INFO;
        } else {
            scope = SCOP_BASE;
        }
        sb.append("&scope=" + scope);
        if (StringUtils.isNotBlank(req.getActivityId())) {
            sb.append("&activityId=" + req.getActivityId());
        }
        if (StringUtils.isNotBlank(req.getExtension())) {
            sb.append("&extension=" + req.getExtension());
        }
        try {
            String redirect_uri = URLEncoder.encode(sb.toString(), "UTF-8");
            log.info("appId - > {} scope - >{} redirect_uri - >{}", req.getAppId(), scope, redirect_uri);
            String re = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + req.getAppId() + "&redirect_uri="
                    + redirect_uri + "&response_type=code&scope="
                    + scope + "&state=STATE#wechat_redirect";
            log.info(":>>> getWechatH5CodeUrlByAppId:{}", re);
            return re;
        } catch (UnsupportedEncodingException e) {
            log.error(":>>> 获取解码错误", e);
            return "";
        }

    }

    @Override
    public String authorizeCode(WechatH5CodeReqVO req) {
        String appId = req.getAppId();
        String extension = req.getExtension();
        String activityId = req.getActivityId();
        try {
            String redirectUri =
                    URLEncoder.encode(bizCompetitionFrontDomain + "/contest-h5/index.html?#/pages/user-login?appId=" + req.getAppId() +
                            "&scope=" + SCOP_USER_INFO + "&extension=" + extension + "&activityId=" + activityId, "UTF-8");
            return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri="
                    + redirectUri + "&response_type=code&scope="
                    + SCOP_USER_INFO + "&state=STATE#wechat_redirect";
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String getWechatH5Code(String wxAppId, String activityId, Integer type, String scope, Integer rewardType, String extension, String version) {
        log.info(":>>> 开始获取微信H5 Code链接");
        try {
            //公众号登录无活动ID情况
            if (Objects.nonNull(type) && type.equals(1000)) {
                return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxAppId + "&redirect_uri="
                        + URLEncoder.encode(bizCompetitionFrontDomain + "/h5/index" +
                        ".html?#/pages/user-login?appId" +
                        "=" + wxAppId + "&extension=" + extension, "UTF-8") +
                        "&response_type" +
                        "=code" +
                        "&scope="
                        + "snsapi_userinfo" + "&state=STATE#wechat_redirect";
            }
            //英语竞赛登录
            if (Objects.nonNull(type) && type.equals(999)) {
                return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxAppId + "&redirect_uri="
                        + URLEncoder.encode(bizCompetitionFrontDomain + "/contest-h5/index" +
                        ".html?#/pages/user-login?appId" +
                        "=" + wxAppId + "&extension=" + extension + "&activityId=" + activityId, "UTF-8") +
                        "&response_type" +
                        "=code" +
                        "&scope="
                        + "snsapi_userinfo" + "&state=STATE#wechat_redirect";
            }
            ActivityInfo activityInfo = activityService.findOne(activityId);
            //目录
            String profile = environment.getProperty("spring.profiles.active");
            String cate = "";
            if ("beta".equals(profile)) {
                cate = "/h4-beta/";
            } else {
                cate = platformConfigService.getCatalogue();
            }
            if ("vote".equals(activityInfo.getType())) {
                cate = platformConfigService.getVoteCatalogue();
            }
            // 非静默授权
            if (StringUtils.isBlank(scope) || scope.equals(SCOP_USER_INFO)) {
                return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxAppId + "&redirect_uri="
                        + URLEncoder.encode(platformConfigService.getBizFrontDomain() + cate + "#/pages/user-login?activityId=" + activityId + "&appId" +
                        "=" + wxAppId + "&type=" + type + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8") +
                        "&response_type" +
                        "=code" +
                        "&scope="
                        + "snsapi_userinfo" + "&state=STATE#wechat_redirect";
            }
            // 静默授权

            if ("beta".equals(profile)) {
                cate = "/h4-beta/";
            } else {
                cate = platformConfigService.getPayCatalogue();
            }
            if ("v2".equals(version)) {
                String redirect_uri =
                        URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward/index?activityId=" + activityId +
                                "&appId=" + wxAppId + "&scope=" + scope + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8");
                return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxAppId + "&redirect_uri="
                        + redirect_uri + "&response_type=code&scope="
                        + scope + "&state=STATE#wechat_redirect";
            } else {
                String redirect_uri =
                        URLEncoder.encode(platformConfigService.getBizFrontPayDomain() + cate + "#/pages/activity-reward?activityId=" + activityId +
                                "&appId=" + wxAppId + "&scope=" + scope + "&rewardType=" + rewardType + "&extension=" + extension, "UTF-8");
                return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wxAppId + "&redirect_uri="
                        + redirect_uri + "&response_type=code&scope="
                        + scope + "&state=STATE#wechat_redirect";
            }

        } catch (Exception e) {
            log.error(":>>> 获取解码错误:{}", e);
            return "";
        }
    }

    @Autowired
    private WechatService wechatService;

    @Override
    public UserResponse getWechatH5AccessToken(String code, String appId, String activityId, Integer type,
                                               String scope, Integer rewardType, String extension, String version) {
        log.info("getWechatH5AccessToken 获取微信H5的AccessToken with code:{},appId:{},activityId:{},scope:{}", code, appId, activityId, scope);
        UserResponse response = new UserResponse();
        try {
            WechatAccessToken token = wechatService.getAccessToken(appId, code);
            log.info("getWechatH5AccessToken 获取的token:{}", JSON.toJSONString(token));
            if (Objects.isNull(token)) {
                log.error("getWechatH5AccessToken 获取失败");
                return null;
            }
            if (Objects.nonNull(token.getErrcode()) && (token.getErrcode().equals(40029) || token.getErrcode().equals(40163))) {
                log.error("getWechatH5AccessToken 获取失败");
                throw new WechatException(808, this.getWechatH5Code(appId, activityId, type, scope, rewardType, extension, version));
            }
            if (StringUtils.isAnyBlank(token.getOpenId(), token.getAccessToken())) {
                log.error("getWechatH5AccessToken 获取失败");
                return null;
            }
            String userId = "";
            WechatH5UserPO wechatH5UserPO = cacheUserService.findByOpenId(appId, token.getOpenId());
            if (Objects.isNull(wechatH5UserPO)) {
                log.info(":>>> 用户不存在:{},开始查询相同unionId:{} 用户", token.getOpenId(), token.getUnionId());
                WechatH5UserPO sameUni = null;
                String sameUnionIdAccountId = "";
                if (StringUtils.isNotBlank(token.getUnionId())) {
                    Query queryUni = new Query().addCriteria(Criteria.where("unionId").is(token.getUnionId()));
                    sameUni = mongoTemplate.findOne(queryUni, WechatH5UserPO.class);
                    log.info(":>>> WechatH5UserPO 相同unionId:{},信息为:{}", token.getUnionId(), JSON.toJSONString(sameUni));
                    if (Objects.nonNull(sameUni) && StringUtils.isNotBlank(sameUni.getUnionId())) {
                        sameUnionIdAccountId = sameUni.getAccountInfoId();
                        log.info(":>> 查询到相同unionId,wechatH5 用户:{},appId:{}", token.getUnionId(), sameUni.getAppId());
                        UserPO u = cacheUserService.findById(sameUnionIdAccountId);
                        log.info(":>>> 相同unionId账户信息为:{},{}", sameUni.getUnionId(), JSON.toJSONString(u));
                        if (Objects.isNull(u)) {
                            sameUnionIdAccountId = null;
                            log.warn(":>>> 账户:{},不存在，账户可能存在异常", sameUnionIdAccountId);
                        }
                    } else {
                        log.info(":>> 没有查询到相同unionId,从wechatMini 用户中查找,wechatH5 unionId:{}", token.getUnionId());
                        WechatMiniUserPO wechatMiniUserPO = mongoTemplate.findOne(queryUni, WechatMiniUserPO.class);
                        if (Objects.nonNull(wechatMiniUserPO) && StringUtils.isNotBlank(wechatMiniUserPO.getUnionId())) {
                            log.info(":>> 没有查询到相同unionId,从wechatMini 用户中查找,查到的账户信息:{},wechatH5 用户:{}", wechatMiniUserPO.getAccountInfoId(), token.getUnionId());
                            sameUnionIdAccountId = wechatMiniUserPO.getAccountInfoId();
                            UserPO u = cacheUserService.findById(sameUnionIdAccountId);
                            log.info(":>>> 相同unionId账户信息为:{},{}", wechatMiniUserPO.getUnionId(), JSON.toJSONString(u));
                            if (Objects.isNull(u)) {
                                sameUnionIdAccountId = null;
                                log.warn(":>>> 账户:{},不存在，账户可能存在异常", sameUnionIdAccountId);
                            }
                        }
                    }
                }
                UserPO userPO = null;
                if (StringUtils.isBlank(sameUnionIdAccountId)) {
                    log.info(":>>> 无相同unionId:{}", token.getUnionId());
                    userPO = cacheUserService.queryUserByOpenId(token.getOpenId());
                    if (Objects.isNull(userPO)) {
                        log.info(":>>> 账户信息不存在:{},新建账户", token.getOpenId());
                        userPO = new UserPO();
                        userPO.setOpenId(token.getOpenId());
                        userPO.setCreatedTime(new Date());
                        userPO.setCountry(token.getCountry());
                        userPO.setProvince(token.getProvince());
                        userPO.setCity(token.getCity());
                        userPO.setUnionId(token.getUnionId());
                        userPO = mongoTemplate.insert(userPO);
                        userId = userPO.getId();
                    } else {
                        log.info(":>>> 账户信息已经存在:{}", token.getOpenId());
                        userId = userPO.getId();
                    }
                } else {
                    userPO = new UserPO();
                    userId = sameUnionIdAccountId;
                    log.info(":>>> 有相同unionId:{},使用账号:{}", token.getUnionId(), sameUnionIdAccountId);
                }
                wechatH5UserPO = new WechatH5UserPO();
                wechatH5UserPO.setOpenId(token.getOpenId());
                wechatH5UserPO.setCreatedTime(new Date());
                wechatH5UserPO.setAccountInfoId(userId);
                wechatH5UserPO.setCountry(token.getCountry());
                wechatH5UserPO.setProvince(token.getProvince());
                wechatH5UserPO.setCity(token.getCity());
                wechatH5UserPO.setUnionId(token.getUnionId());
                wechatH5UserPO.setAppId(appId);
                wechatH5UserPO.setValidLogo(false);
                wechatH5UserPO = mongoTemplate.insert(wechatH5UserPO);

                BeanUtils.copyProperties(wechatH5UserPO, userPO);
                userPO.setId(wechatH5UserPO.getAccountInfoId());
                response.setUserPO(userPO);
                if (StringUtils.isEmpty(scope) || scope.equals(SCOP_USER_INFO)) {
                    UserResponse rep = wechatH5UserService.getUserInfo(token.getAccessToken(), token.getOpenId(), wechatH5UserPO.getId());
                    response.setAvatarUrl(rep.getAvatarUrl());
                    response.setNickName(rep.getNickName());
                }
                try {
                    UserPO userExt = new UserPO();
                    userExt.setId(userId);
                    userExt.setOpenId(token.getOpenId());
                    userExt.setUnionId(token.getUnionId());
                    userExt.setNickName(response.getNickName());
                    userExt.setAvatarUrl(response.getAvatarUrl());
                    userExt.setH5SignUp(Boolean.TRUE);
                    userInfoExtendService.saveOrUpdate(userExt);
                } catch (Exception e) {
                    log.error("【更行用户扩展信息错误】-> {}", userId, e);
                }

            } else {
                boolean checkLogo = wechatH5UserPO.getValidLogo() != null && !wechatH5UserPO.getValidLogo();
                boolean isNotQiniu = StringUtils.isBlank(wechatH5UserPO.getAvatarUrl()) || wechatH5UserPO.getAvatarUrl().startsWith("https://thirdwx.qlogo.cn");
                if (scope.equals(SCOP_USER_INFO) && (checkLogo || isNotQiniu)) {
                    // 更新微信头像
                    UserResponse rep = wechatH5UserService.getUserInfo(token.getAccessToken(), token.getOpenId(), wechatH5UserPO.getId());
                    response.setAvatarUrl(rep.getAvatarUrl());
                    response.setNickName(rep.getNickName());
                    wechatH5UserPO.setAvatarUrl(rep.getAvatarUrl());
                    wechatH5UserPO.setValidLogo(true);
                    wechatH5UserPO.setNickName(rep.getNickName());
                }
                if (StringUtils.isBlank(wechatH5UserPO.getUnionId()) && scope.equals(SCOP_USER_INFO)) {
                    log.info(":>>> 微信用户:{},的unionId为空:更新新的unionId:{}", wechatH5UserPO.getId(), token.getUnionId());
                    Query query1 = new Query().addCriteria(Criteria.where("id").is(wechatH5UserPO.getId()));

                    WechatH5UserPO sameUni = null;
                    userId = wechatH5UserPO.getAccountInfoId();
                    if (StringUtils.isNotBlank(token.getUnionId())) {
                        Query queryUni = new Query().addCriteria(Criteria.where("unionId").is(token.getUnionId()));
                        sameUni = mongoTemplate.findOne(queryUni, WechatH5UserPO.class);
                        if (Objects.nonNull(sameUni) && StringUtils.isNotBlank(sameUni.getAccountInfoId())) {
                            Update updateSameU = new Update();
                            updateSameU.set("unionId", token.getUnionId());
                            updateSameU.set("accountInfoId", sameUni.getAccountInfoId());
                            updateSameU.set("oldAccountInfoId", wechatH5UserPO.getAccountInfoId());
                            if (StringUtils.isBlank(wechatH5UserPO.getAvatarUrl()) && StringUtils.isNotBlank(sameUni.getAvatarUrl())) {
                                updateSameU.set("avatarUrl", sameUni.getAvatarUrl());
                            }
                            if (StringUtils.isBlank(wechatH5UserPO.getNickName()) && StringUtils.isNotBlank(sameUni.getNickName())) {
                                updateSameU.set("nickName", sameUni.getNickName());
                            }
                            this.mongoTemplate.updateFirst(query1, updateSameU, WechatH5UserPO.class);
                            cacheUserService.removeWechatH5UserOpenId(token.getOpenId());
                            cacheUserService.removeUserId(wechatH5UserPO.getAccountInfoId());
                            log.info(" 更新登录用户->{} AccountInfoId ->{}，-> oldAccountInfoId", token.getOpenId(), sameUni.getAccountInfoId(), wechatH5UserPO.getAccountInfoId());
                            userId = sameUni.getAccountInfoId();
                            wechatH5UserPO.setAccountInfoId(userId);
                            wechatH5UserPO.setAvatarUrl(sameUni.getAvatarUrl());
                            wechatH5UserPO.setNickName(sameUni.getNickName());
                        } else {
                            Update update = new Update();
                            update.set("unionId", token.getUnionId());
                            mongoTemplate.updateFirst(query1, update, WechatH5UserPO.class);
                        }
                    }
                }
            }
            redisTemplate.opsForValue().set(String.format(TOKEN_KEY_WECHAT_H5, token.getOpenId()),
                    token.getAccessToken(), 24, TimeUnit.HOURS);
            if (StringUtils.isBlank(response.getAvatarUrl())) {
                response.setAvatarUrl(wechatH5UserPO.getAvatarUrl());
            }
            if (StringUtils.isBlank(response.getNickName())) {
                response.setNickName(wechatH5UserPO.getNickName());
            }
            response.setId(wechatH5UserPO.getAccountInfoId());
            response.setName(wechatH5UserPO.getName());
            response.setOpenId(wechatH5UserPO.getOpenId());
            response.setToken(WebTokenUtils.signToken(response.getOpenId()));
            redisTemplate.opsForValue().set(this.getAccessTokenPreFix(appId, wechatH5UserPO.getOpenId()), WebTokenUtils.signToken(wechatH5UserPO.getOpenId()), 360, TimeUnit.DAYS);
            log.info(":>>> 返回的登陆数据:{}", JSON.toJSONString(response));
            response.setPlatform("wechat-h5");
            log.info(":>>> 微信用户的数据:{}", JSON.toJSONString(wechatH5UserPO));
            if (Objects.nonNull(wechatH5UserPO.getCheckedMobile())) {
                response.setBindMobile(wechatH5UserPO.getCheckedMobile());
            }
            if (StringUtils.isNotBlank(wechatH5UserPO.getMobile())) {
                response.setMobile(wechatH5UserPO.getMobile());
            }
            if (StringUtils.isNotBlank(wechatH5UserPO.getProvince())) {
                response.setProvince(wechatH5UserPO.getProvince());
            }
            if (StringUtils.isNotBlank(wechatH5UserPO.getCity())) {
                response.setCity(wechatH5UserPO.getCity());
            }
            if (StringUtils.isNotBlank(wechatH5UserPO.getArea())) {
                response.setArea(wechatH5UserPO.getArea());
            }
            if (StringUtils.isNotBlank(wechatH5UserPO.getFullAddress())) {
                response.setAddresss(wechatH5UserPO.getFullAddress());
            }
            boolean get = activityService.isGetReward(activityId, userId);
            response.setRewarded(get);
            return response;
        } catch (BusinessException e) {
            log.error(":>>> 登陆业务异常:{}", e.getMessage());
            throw e;
        } catch (WechatException w) {
            log.error(":>>> 登陆异常:{}", w.getMessage());
            throw w;
        } catch (Exception e) {
            log.error(":>>> 登陆异常:{}", e);
            throw new BusinessException("微信登录异常,请左上角刷新页面");
        }

    }

    public String getUserId(WechatAccessToken token, UserResponse response, boolean isScopUserInfo) {
        if (StringUtils.isNotBlank(token.getUnionId())) {
            Query queryUni = new Query().addCriteria(Criteria.where("unionId").is(token.getUnionId()));
            WechatH5UserPO wechatH5UserPO = mongoTemplate.findOne(queryUni, WechatH5UserPO.class);
            if (Objects.nonNull(wechatH5UserPO)) {
                log.info("getUserBase echatH5UserPO has same unionId:{}", token.getUnionId());
                return wechatH5UserPO.getAccountInfoId();
            }
            WechatMiniUserPO wechatMiniUserPO = mongoTemplate.findOne(queryUni, WechatMiniUserPO.class);
            if (Objects.nonNull(wechatMiniUserPO)) {
                log.info("getUserBase WechatMiniUserPO has same unionId:{}", token.getUnionId());
                return wechatMiniUserPO.getAccountInfoId();
            }
            UserPO userPO = mongoTemplate.findOne(queryUni, UserPO.class);
            if (Objects.nonNull(userPO)) {
                log.info("getUserBase UserPO has same unionId:{}", token.getUnionId());
                return userPO.getId();
            }
        }
        log.info("getUserBase not has unionId");
        UserPO userPO = cacheUserService.queryUserByOpenId(token.getOpenId());
        if (userPO != null) {
            log.info("getUserBase UserPO unionid is null,has openId:{}", token.getOpenId());
            return userPO.getId();
        }
        userPO = new UserPO();
        userPO.setOpenId(token.getOpenId());
        userPO.setCreatedTime(new Date());
        userPO.setCountry(token.getCountry());
        userPO.setProvince(token.getProvince());
        userPO.setCity(token.getCity());
        userPO.setUnionId(token.getUnionId());
        if (isScopUserInfo) {
            userPO.setAvatarUrl(response.getAvatarUrl());
            userPO.setNickName(response.getNickName());
        }
        userPO = mongoTemplate.insert(userPO);
        return userPO.getId();
    }

    @Override
    public boolean checkMobile(CheckMobileVO req) {
        String paltform = req.getUserPO().getCurrentPlatform();
        String mobile = req.getMobile();
        log.info(":>>> 开始验证手机号:{}, platform  ->{}", JSON.toJSONString(req), paltform);
        Date date = new Date();
        SmsBaseInfoDTO dto = SmsBaseInfoDTO.builder().phone(req.getMobile()).code(req.getCode()).build();
        Pair<Boolean, String> re = smsServerService.checkVerifyCode(dto);
        if (!re.getLeft()) {
            throw new BusinessException(re.getRight());
        }
        log.info(":>>> 手机号:{}验证结果:{}", mobile, JSON.toJSONString(re));
        UserPO userPO = req.getUserPO();
        String accountInfoId = userPO.getId();
        Update update = new Update();
        update.set("mobile", req.getMobile());
        update.set("binded", true);
        update.set("checkedMobile", true);
        update.set("updatedTime", date);
        update.set("checkedMobileTime", date);
        //账户表中是否已绑定
        if (StringUtils.isBlank(userPO.getMobile())) {
            Query query = new Query().addCriteria(Criteria.where("id").is(accountInfoId));
            mongoTemplate.updateFirst(query, update, UserPO.class);
            this.cacheUserService.removeUserId(accountInfoId);
            this.cacheUserService.removeUserOpenId(userPO.getOpenId());
        }
        Query query = null;
        if (paltform.equals("wechat-h5")) {
            visitorUserService.updateAccountMobile(accountInfoId, mobile);
            query = new Query().addCriteria(Criteria.where("accountInfoId").is(accountInfoId));
            UpdateResult updateResult = mongoTemplate.updateMulti(query, update, WechatH5UserPO.class);
            log.info(":>>>platform->{} 更新账户:{},用户绑定手机号数量:{}", paltform, req.getUserPO().getId(),
                    updateResult.getModifiedCount());
            this.cacheUserService.updateByAccountId(accountInfoId);
            return re.getLeft();
        }
        // 更新QQ
        visitorUserService.updateAccountMobile(accountInfoId, mobile);
        String openId = userPO.getOpenId();
        query = new Query();
        query.addCriteria(Criteria.where("openId").is(openId));
        query.addCriteria(Criteria.where("accountInfoId").is(accountInfoId));
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, QQH5UserPO.class);
        log.info("qq-h5用户手机号 : {} openId - > {}, accountId - > {} 更新绑定 {}", mobile, openId, accountInfoId, updateResult.getModifiedCount());
        return true;
    }

    @Override
    public boolean sendSms(CheckMobileVO req) {
        log.info(":>>> 开始发送短信验证码:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getMobile())) {
            log.error(":>>> 开始发送短信验证码");
            throw new BusinessException("开始发送短信验证码参数错误");
        }
        SmsTemplate smsTemplate = smsTemplateService.findBySmsTemplate("", req.getSign(), "code");
        if (Objects.isNull(smsTemplate) || StringUtils.isBlank(smsTemplate.getSign()) || Objects.isNull(smsTemplate.getTempalteId())) {
            log.error(":>>> 短信模版配置错误:没有配置");
            throw new BusinessException("短信模版配置错误");
        }
        // 消息类型 0发送短信、非0发送发送语音
        SmsBaseInfoDTO.SmsType smsType = SmsBaseInfoDTO.SmsType.getTypeByValue(req.getVerifyCodeType());
        String templeteId = SmsBaseInfoDTO.SmsType.SMS.equals(smsType) ? String.valueOf(smsTemplate.getTempalteId()) : smsTemplate.getVoiceTempalteId();
        SmsBaseInfoDTO dto = SmsBaseInfoDTO.builder()
                .smsType(smsType)
                .phone(req.getMobile())
                .templateId(templeteId)
                .signName(smsTemplate.getSign())
                .build();
        // 发送消息
        Pair<Boolean, String> result = smsServerService.sendSmsMsg(dto);
        if (!result.getLeft()) {
            throw new BusinessException(result.getRight());
        }
        return true;
    }


    private void updateUser(UserPO user, UserInfoRequest userInfo) {
        log.info(":>>> 开始更新用户信息:{}", JSON.toJSONString(user));
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(user.getId()));
        Update update = new Update();
        boolean isUpdate = false;
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getGender()) &&
                (StringUtils.isBlank(user.getGender()) || !user.getGender().equals(userInfo.getGender()))) {
            update.set("gender", userInfo.getGender());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getAvatarUrl()) &&
                (StringUtils.isBlank(user.getAvatarUrl()) || !user.getAvatarUrl().equals(userInfo.getAvatarUrl()))) {
            update.set("avatarUrl", userInfo.getAvatarUrl());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getNickName()) &&
                (StringUtils.isBlank(user.getNickName()) || !user.getNickName().equals(userInfo.getNickName()))) {
            update.set("nickName", userInfo.getNickName());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getUniversityValue()) &&
                (StringUtils.isBlank(user.getUniversityValue()) || !user.getUniversityValue().equals(userInfo.getUniversityValue()))) {
            update.set("universityValue", userInfo.getUniversityValue());
            update.set("universityName", userInfo.getUniversityName());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getMajor()) &&
                (StringUtils.isBlank(user.getMajor()) || !user.getMajor().equals(userInfo.getMajor()))) {
            update.set("major", userInfo.getMajor());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getMobile()) &&
                (StringUtils.isBlank(user.getMobile()) || !user.getMobile().equals(userInfo.getMobile()))) {
            update.set("mobile", userInfo.getMobile());
            update.set("gainMobileTime", new Date());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getQqNumber()) &&
                (StringUtils.isBlank(user.getQqNumber()) || !user.getQqNumber().equals(userInfo.getQqNumber()))) {
            update.set("qqNumber", userInfo.getQqNumber());
            isUpdate = true;
        }
        if (!org.springframework.util.StringUtils.isEmpty(userInfo.getName()) &&
                (StringUtils.isBlank(user.getName()) || !user.getName().equals(userInfo.getName()))) {
            update.set("name", userInfo.getName());
            isUpdate = true;
        }

        if (StringUtils.isNotBlank(userInfo.getSignature()) && !userInfo.getSignature().equals(user.getSignature())) {
            update.set("signature", userInfo.getSignature());
            isUpdate = true;
        }
        if (userInfo.getLabels() != null && userInfo.getLabels().size() > 0) {
            update.set("labels", userInfo.getLabels());
        }
        if (StringUtils.isNotBlank(userInfo.getUniversityId())) {
            update.set("universityId", userInfo.getUniversityId());
            isUpdate = true;
        }
        if (StringUtils.isNotBlank(userInfo.getProvince())) {
            update.set("province", userInfo.getProvince());
            isUpdate = true;
        }
        if (StringUtils.isNotBlank(userInfo.getAddress())) {
            update.set("fullAddress", userInfo.getAddress());
            isUpdate = true;
        }
        if (isUpdate) {
            this.mongoTemplate.updateFirst(query, update, UserPO.class);
        }
        if ("wechat-mini".equals(userInfo.getPlatform())) {
            Query queryMini = new Query();
            queryMini.addCriteria(Criteria.where("accountInfoId").is(user.getId()));
            Update update1 = new Update();
            if (StringUtils.isNotBlank(userInfo.getAvatarUrl())) {
                update1.set("avatarUrl", userInfo.getAvatarUrl());
            }
            if (StringUtils.isNotBlank(userInfo.getNickName())) {
                update1.set("nickName", userInfo.getNickName());
            }
            update1.set("updatedTime", new Date());
            mongoTemplate.updateFirst(queryMini, update1, WechatMiniUserPO.class);
            log.info(":>>> 更新小程序用户:{},头像和昵称", user.getId());
        }
        if ("app".equals(userInfo.getPlatform())) {
            Query queryMini = new Query();
            queryMini.addCriteria(Criteria.where("accountInfoId").is(user.getId()));
            Update update1 = new Update();
            if (StringUtils.isNotBlank(userInfo.getAvatarUrl())) {
                update1.set("avatarUrl", userInfo.getAvatarUrl());
            }
            if (StringUtils.isNotBlank(userInfo.getNickName())) {
                update1.set("nickName", userInfo.getNickName());
            }
            update1.set("updatedTime", new Date());
            mongoTemplate.updateFirst(queryMini, update1, AppUserPO.class);
            log.info(":>>> 更新app用户用户:{},头像和昵称", user.getId());
        }
    }

    private Integer getIntegerRandom(Integer max, Integer min) {
        Random rand = new Random();
        int i = rand.nextInt(max - min + 1) + min;
        log.info(":>>> 生成的随机数为:{}", i);
        return i;

    }

    private String getWechatCode(String appId, String codeTodo) {
        String code = redisTemplate.opsForValue().get(String.format(CODE_WECHAT_H5_PRE_FIX, appId, codeTodo));
        log.info(":>>> 微信公众号H5:{},code:{}", appId, codeTodo);
        return code;
    }

    private boolean hasWechatCode(String appId, String codeTodo) {
        String c = this.getWechatCode(appId, codeTodo);
        if (StringUtils.isBlank(c)) {
            log.info(":>>> code 不存在:{}", codeTodo);
            return true;
        }
        return false;
    }

    @Override
    public String getAccessTokenPreFix(String appId, String openId) {
        String key = String.format(ACCESS_TOKEN_WECHAT_H5_PRE_FIX, appId, openId);
        return key;
    }

    @Override
    public UserResponse codeLogin(CheckMobileVO req) {
        log.info(":>>> 开始手机验证码登陆:{}", JSON.toJSONString(req));
        SmsBaseInfoDTO dto = SmsBaseInfoDTO.builder().phone(req.getMobile()).code(req.getCode()).build();
        Pair<Boolean, String> re = smsServerService.checkVerifyCode(dto);
        if (!re.getLeft()) {
            throw new BusinessException(re.getRight());
        }
        log.info(":>>> 手机号:{},验证码结果:{}", req.getMobile(), JSON.toJSONString(re));
        UserPO userPO = visitorUserService.find(req.getMobile());
        //默认手机号为openId
        if (Objects.isNull(userPO)) {
            userPO = visitorUserService.createUser(req);
        }
        String openId = userPO.getOpenId();
        UserResponse userResponse = new UserResponse();
        userResponse.setOpenId(openId);
        String token = WebTokenUtils.signToken(openId);
        userResponse.setToken(token);
        userResponse.setAuthorized(false);
        //token缓存
        String userToken = "user_token:" + token;
        redisTemplate.opsForValue().set(userToken, userPO.getId(), 7L, TimeUnit.DAYS);
        return userResponse;
    }

    @Override
    public boolean updateUserInfo(UserOtherInfo userOtherInfo, String platform) {
        log.info(":>>> 修改用户信息 platform:{},userOtherInfo:{}", platform, JSON.toJSONString(userOtherInfo));
        if (Objects.isNull(userOtherInfo) || StringUtils.isBlank(platform) || StringUtils.isBlank(userOtherInfo.getUserId())) {
            log.error(":>>> 参数信息为空");
            return false;
        }
        UserOtherInfo userOtherInfo1 = userOtherInfoService.findByUserId(userOtherInfo.getUserId());
        if (Objects.isNull(userOtherInfo1)) {
            userOtherInfoService.create(userOtherInfo);
        } else {
            userOtherInfoService.update(userOtherInfo);
        }
        // TODO:更新排行榜头像
        switch (platform) {
            case "wechat-h5":
                wechatH5UserService.updateOtherInfo(userOtherInfo);
                cacheUserService.removeWechatH5UserOpenId(userOtherInfo.getOpenId());
                break;
            case "qq":
                this.updateQQMiniOtherInfo(userOtherInfo);
                break;
            case "qq-h5":
                qQUserService.updateOtherInfo(userOtherInfo);
                break;
            case "h5":
                h5UserPOService.updateOtherInfo(userOtherInfo);
                break;
            case "wechat-mini":
                wechatMiniService.updateOtherInfo(userOtherInfo);
                break;
            case "app":
                //TODO:
                break;
            case "wechat-third":
                //TODO:
                break;
            default:
                break;
        }
        rankingServiceImpl.updateUserOtherInfo(userOtherInfo);
        return true;
    }

    @Async
    @Override
    public void updateQQMiniOtherInfo(UserOtherInfo u) {
        if (Objects.isNull(u) || StringUtils.isBlank(u.getUserId())) {
            log.error(":>>> 修改用户其他信息参数错误");
            return;
        }
        Query query = new Query().addCriteria(Criteria.where("accountInfoId").is(u.getUserId()));
        Update up = new Update();
        boolean doIt = false;
        if (StringUtils.isNotBlank(u.getNickName())) {
            up.set("nickName", u.getNickName());
            doIt = true;
        }
        if (StringUtils.isNotBlank(u.getAvatarUrl())) {
            up.set("avatarUrl", u.getAvatarUrl());
            doIt = true;
        }
        if (doIt) {
            up.set("updatedTime", new Date());
            UpdateResult updateResult = mongoTemplate.updateMulti(query, up, UserPO.class);
            log.info(":>>> 修改QQ小程序用户信息结果:{},{}", u.getUserId(), updateResult.getModifiedCount());
        }

    }

    @Override
    public void updateUserInfo(UserPO req) {
        log.info(":>>> 开始修改用户信息:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId())) {
            log.error(":>>> 修改用户信息错误");
            throw new BusinessException("修改用户信息错误参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        boolean toUpdate = false;
        Update update = new Update();
        if (StringUtils.isNotBlank(req.getNickName())) {
            update.set("nickName", req.getNickName());
            toUpdate = true;
        }
        if (StringUtils.isNotBlank(req.getBirthDate())) {
            update.set("birthDate", req.getBirthDate());
            toUpdate = true;
        }
        if (StringUtils.isNotBlank(req.getAvatarUrl())) {
            update.set("avatarUrl", req.getAvatarUrl());
            toUpdate = true;
        }
        if (StringUtils.isNotBlank(req.getUniversityId())) {
            UniversityInfo universityInfo = universityService.get(req.getUniversityId());
            update.set("universityName", universityInfo.getName());
            update.set("universityId", universityInfo.getId());
            update.set("universityLogo", universityInfo.getLogo());
            toUpdate = true;
        }
        if (StringUtils.isNotBlank(req.getGender())) {
            update.set("gender", req.getGender());
        }
        if (StringUtils.isNotBlank(req.getMobile())) {
            update.set("mobile", req.getMobile());
            toUpdate = true;
        }
        if (Objects.nonNull(req.getCheckedMobile())) {
            update.set("checkedMobile", req.getCheckedMobile());
            toUpdate = true;
        }
        if (Objects.nonNull(req.getCheckedMobileTime())) {
            update.set("checkedMobileTime", req.getCheckedMobileTime());
            toUpdate = true;
        }
        if (!toUpdate) {
            return;
        }
        update.set("updatedTime", new Date());
        mongoTemplate.updateFirst(query, update, UserPO.class);
        mongoTemplate.updateFirst(query, update, UserInfoExtend.class);
        cacheUserService.removeUserId(req.getId());
    }
}
